Skip to content

wallet: Require the recorded fingerprint before import - #57

Open
BenWestgate wants to merge 1 commit into
reviewability-v1from
30-recorded-fingerprint-gate
Open

BenWestgate wants to merge 1 commit into
reviewability-v1from
30-recorded-fingerprint-gate

Conversation

@BenWestgate

@BenWestgate BenWestgate commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Fixes #30. Library/CLI half of #26; GUI half is tracked separately.

This adds the restore-time wallet identity gate before Bitcoin Core mutation.

  • BitcoinCore.initialize() accepts an expected fingerprint and checks it before wallet selection, unlock, creation or import.
  • ms32 wallet requires the fingerprint from the wallet record; mismatch retries without changing Core.
  • the recovered fingerprint is not shown before that typed-record gate, including during correction; choosing the no-record fallback is an explicit disclosure path, and declining it ends that restore attempt rather than returning to record-based verification;
  • ms32 create --existing uses the same restore gate and does not expose the recovered fingerprint through correction or rendering before the independent record/no-record decision;
  • wallet: Check existing seed before sharing #81 is the focused follow-up that moves the create --existing record/no-record decision earlier, immediately after parsing the existing seed and before any new share ceremony or output;
  • with no wallet record, the CLI shows the recovered fingerprint, backup identifier and codex32/Bails/Bails-alpha identifier-origin result, then requires the warning/confirmation path;
  • fresh ms32 create only records the new fingerprint; it has no pre-existing wallet identity to authenticate;
  • parse_fingerprint() accepts 8 hex digits in any case or spacing.

#43 tracks checksummed wallet-record fields; #55 tracks the separately planned encrypted full-descriptor backup; #56 tracks identifier-assisted correction ranking. The current release gate is accident safety, not malicious-share-tampering resistance.

Review shape

Current head 054e8d9 is the same Ben Westgate-authored restore-authentication patch replayed directly onto current #42 (128bda4). Its stable patch-id is identical to the previously reviewed a7efaae / 37eef4d patch; the replay changes no restore behavior.

The current #42 tip is 5,092 installed production logical lines. Adding this unchanged restore patch produces 5,196 lines, keeping the intermediate commit within the maintainer-authorized <5200 gate without raising the cap or taking a security-sensitive refactor.

Security verification

  • BitcoinCore.initialize() calls verify_identity() before _select() or any wallet RPC/mutation;
  • test_identity_mismatch_stops_before_any_wallet_call passes and asserts the RPC call log stays empty on mismatch;
  • test_no_record_is_the_operators_choice_and_checks_nothing passes, preserving the explicit fallback contract;
  • CLI source keeps the recovered fingerprint hidden until the independent record/no-record decision for both ms32 wallet and ms32 create --existing.

Validation on 054e8d9

  • focused identity/fingerprint/existing/timestamp regressions: 40 passed;
  • full suite: 926 passed normally;
  • full suite: 926 passed under python -O;
  • strict mypy: clean across 21 source files;
  • Ruff check and format check: clean;
  • correction constants verification: pass;
  • frozen differential correction verifier: all 57 cases pass;
  • git diff --check: clean;
  • exact-head Python-package run 622: success;
  • exact-head Bitcoin Core wallet-fixture run 25: success.

Human review/integration order for this stack is #42 → #57 → #105 → #80 → #81 → #95. #45/#46 are already contained in #42's head branch; #7/#51 are already in reviewability-v1.

AI assistance was used for the authorized mechanical rebase/conflict validation and review follow-ups. The focused source commit retains Ben Westgate as its author and still requires responsible human review before integration.

@BenWestgate BenWestgate added area: cli Command-line interface behavior. area: security Security invariants, hardening, and security-sensitive boundaries. area: wallet/core Wallet integration and Bitcoin Core boundaries. gate: adversarial review Resolve, merge, or explicitly defer before the next full adversarial review. labels Sep 24, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5eaf535bb7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/codex32/_bitcoin_core.py Outdated
Comment thread src/codex32/_bitcoin_core.py Outdated
@BenWestgate

Copy link
Copy Markdown
Owner Author

Contract clarification applied in b50321a: ms32 create no longer treats the new fingerprint as authentication evidence or asks the user to type it back. It only requires acknowledging that the fingerprint was written on the wallet record. ms32 wallet retains the fingerprint/no-record restore gate. 886 normal + 886 optimized tests pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6da1f2a416

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/security/invariants.md

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review (Claude), posted at the maintainer's request. I wrote 5eaf535, so this is partly self-review.

Not ACKing 6da1f2a.

  • 6da1f2a removes identifier_origin/identifier_note, which the no-record path was built to show. If that's intended, invariants.md:16-18 and model.md:232 still promise it (agree with the Codex P2). If not, revert it. Either way #57 now differs from #28, which keeps them.
  • Policy: 6da1f2a is authored by Codex Preflight <codex-preflight@localhost>, and AI_POLICY.md forbids agent authors. Its message also contains a literal \n\n and has no area prefix. 5eaf535 has a Co-Authored-By: Claude trailer, which the same policy forbids. Squash-merge or reword.
  • Q: create --existing imports an existing seed (timestamp 0) with restore=False, so it skips the fingerprint gate. Should it pass restore=True? The Enter-if-none path keeps it usable.

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review, posted at the maintainer's request.

Concept ACK 70a188a. The no-record identifier evidence is restored and matches #28 again.

One correctness item remains: ms32 create --existing supplies an existing seed but still reaches _initialize_wallet(..., restore=False), so it can import without the wallet-record gate. Treat --existing as a restore for wallet initialization.

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review, posted at the maintainer's request.

ACK 795ccdd. Existing-seed initialization now uses the same restore gate before import, including after re-sharing; identifier evidence remains aligned with #28. Full Python package matrix is green.

Copy link
Copy Markdown
Owner Author

Release-gate verification at current head 795ccdd: the authoritative Core boundary calls verify_identity(secret, expected_fingerprint) before _select(), so mismatch occurs before wallet listing/selection, unlock, creation, or descriptor import. The focused regression test_identity_mismatch_stops_before_any_wallet_call asserts the mismatch and rpc.calls == []. The current Python package workflow run 36284182340 completed successfully. This satisfies the verify-before-mutate accident-safety finding for the CLI/library subset; #55 remains the separate malicious-tampering/descriptor-backup design.

Copy link
Copy Markdown
Owner Author

One non-code release-gate item still remains despite the code ACK: the current PR history still contains 5eaf535 with a Co-Authored-By: Claude trailer and 6da1f2a authored/committed by Codex Preflight. docs/developer/AI_POLICY.md says not to include agents as authors or co-authors. Before merge, squash/reword/rebase this branch under the responsible human author while preserving the current 795ccdd tree, then rerun the green package workflow on the rewritten head.

Copy link
Copy Markdown
Owner Author

Release-gate history check: the functional fix is ACKed at 795ccdd, but the commit-policy condition from the earlier review still remains. The branch history still contains 5eaf535 with a Co-Authored-By: Claude ... trailer and 6da1f2a authored by Codex Preflight <codex-preflight@localhost> (later behavior commits correct the code, but do not remove those history records). Before merge, rewrite/squash so the retained release commit is authored by the responsible human and follows AI_POLICY.md. Functionally, the verify-before-mutate boundary and existing-seed restore gate are green.

@BenWestgate
BenWestgate force-pushed the 30-recorded-fingerprint-gate branch from 795ccdd to 53cd58b Compare September 27, 2026 03:21
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@BenWestgate
BenWestgate force-pushed the 30-recorded-fingerprint-gate branch from 53cd58b to dcc0d41 Compare September 27, 2026 03:22
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Owner Author

Release-gate history follow-up: the earlier commit-policy blocker is now resolved. Current head dcc0d41 is a single commit directly on cf1a599, authored and committed by Ben Westgate, with no agent author/co-author history retained. The fresh Python package run 36291219348 on dcc0d41 completed successfully. Functionally this preserves the ACKed 795ccdd recovery-gate tree, so #57 is ready for human review on the CLI/library accident-safety scope.

Copy link
Copy Markdown
Owner Author

Security fix-verification refresh at current head dcc0d41: fixed for the CLI/library verify-before-mutate finding. I re-ran the exact current PR archive: test_identity_mismatch_stops_before_any_wallet_call and test_no_record_is_the_operators_choice_and_checks_nothing both pass, and static inspection confirms BitcoinCore.initialize() calls verify_identity(secret, expected_fingerprint) before _select(), so a mismatch precedes wallet selection/listing, unlock, creation, or descriptor import. The no-record legitimate fallback remains functional. This verifies the accident-safety boundary only; #55 remains the separate malicious-tampering/descriptor-backup design.

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Release-gate ACK 37eef4d.

I re-verified the refreshed one-commit restore-authentication change on top of current #46. BitcoinCore.initialize() calls verify_identity() before _select() or any wallet RPC/mutation; the mismatch regression leaves the RPC call log empty; the explicit no-record fallback remains deliberate; and both ms32 wallet and ms32 create --existing keep the recovered fingerprint hidden until the independent record/no-record decision. All inline review threads are resolved.

Exact-head validation passed 34 focused identity/fingerprint/existing/timestamp regressions, 922 tests normally, 922 under python -O, strict mypy, Ruff check/format, correction-constant verification, all 57 frozen differential cases and git diff --check. I also reran both isolated real-Core checks with Bitcoin Core 32.0rc2: regtest passed and the main-chain smoke fixture passed, both reporting /Satoshi:32.0.0/.

No remaining code blocker from this review. Human order for this line is #42 → #46 → #57 → #80 → #81.

@BenWestgate

Copy link
Copy Markdown
Owner Author

Agent exact-head verification on 37eef4d:

  • traced BitcoinCore.initialize() and confirmed verify_identity() runs before _select() and therefore before wallet selection, unlock, creation, descriptor import, or other wallet RPC mutation;
  • test_identity_mismatch_stops_before_any_wallet_call reproduces the original boundary and passes with an empty RPC call log on mismatch;
  • matching-record, explicit no-record fallback, fresh-create, and create --existing controls pass;
  • the only production caller of BitcoinCore.initialize() is the CLI handoff, which passes the independently obtained expected_fingerprint;
  • focused local verification: 7 passed; exact-head GitHub Python-package and Bitcoin Core wallet-fixture checks are green.

No remaining code blocker found for the recorded-fingerprint-before-mutation finding. Human review order remains #42 → #46 → #57.

Copy link
Copy Markdown
Owner Author

Independent security-fix verification against the original verify-before-mutate finding, rechecked on current head 37eef4d:

  • BitcoinCore.initialize() validates the MasterSeed/account/timestamp and calls verify_identity(secret, expected_fingerprint) before _select() or any wallet RPC/mutation.
  • test_identity_mismatch_stops_before_any_wallet_call supplies a mismatching recorded fingerprint and asserts the RPC call log remains exactly empty.
  • The no-record path is explicitly separate: verify_identity(..., None) performs no fingerprint comparison, and the CLI requires the warning/visual-confirmation path rather than silently treating the recovered value as independent evidence.
  • Existing review threads covering premature fingerprint disclosure and create --existing are resolved; wallet: Check existing seed before sharing #81 is the separate timing follow-up that moves the record/no-record decision before a new share ceremony.

This verifies the release-gate accident-safety claim: a typed-record mismatch cannot select, unlock, create, or import into a Bitcoin Core wallet. It does not claim malicious-share-tampering resistance; #55 remains the stronger encrypted-descriptor design.

BenWestgate pushed a commit that referenced this pull request Oct 1, 2026
Reassign the expected_fingerprint argument instead of copying it into a
local, and give existing_secret its None default before the source
checks instead of in an else branch.

Behavior is unchanged. The installed package drops from 5161 to 5159
logical review lines, which keeps the integrated #7/#42/#57/#46/#80/#81
tip under the <5200 budget.

Security: the record gate still runs before any card is generated or
shown, and interrupts at that gate still raise _WalletSetupInterrupted.

Validation: ruff check, ruff format --check, mypy src/codex32, and
pytest (918 passed, with and without -O).

Refs #81, #38.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018az69UX4773mYohXAtE8kD
Base automatically changed from codex/v1-pre-review-cleanup to codex/37-mixed-case-correction October 1, 2026 06:59
@BenWestgate
BenWestgate force-pushed the 30-recorded-fingerprint-gate branch from 37eef4d to a7efaae Compare October 1, 2026 18:22
BenWestgate pushed a commit that referenced this pull request Oct 1, 2026
Reassign the expected_fingerprint argument instead of copying it into a
local, and give existing_secret its None default before the source
checks instead of in an else branch.

Behavior is unchanged. The installed package drops from 5161 to 5159
logical review lines, which keeps the integrated #7/#42/#57/#46/#80/#81
tip under the <5200 budget.

Security: the record gate still runs before any card is generated or
shown, and interrupts at that gate still raise _WalletSetupInterrupted.

Validation: ruff check, ruff format --check, mypy src/codex32, and
pytest (918 passed, with and without -O).

Refs #81, #38.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018az69UX4773mYohXAtE8kD
BenWestgate pushed a commit that referenced this pull request Oct 1, 2026
Reassign the expected_fingerprint argument instead of copying it into a
local, and give existing_secret its None default before the source
checks instead of in an else branch.

Behavior is unchanged. The installed package drops from 5161 to 5159
logical review lines, which keeps the integrated #7/#42/#57/#46/#80/#81
tip under the <5200 budget.

Security: the record gate still runs before any card is generated or
shown, and interrupts at that gate still raise _WalletSetupInterrupted.

Validation: ruff check, ruff format --check, mypy src/codex32, and
pytest (918 passed, with and without -O).

Refs #81, #38.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018az69UX4773mYohXAtE8kD
@BenWestgate
BenWestgate force-pushed the codex/37-mixed-case-correction branch from 5ffb196 to 1435560 Compare October 1, 2026 19:26
@BenWestgate
BenWestgate force-pushed the 30-recorded-fingerprint-gate branch from a7efaae to 054e8d9 Compare October 1, 2026 22:02
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

BenWestgate pushed a commit that referenced this pull request Oct 1, 2026
Reassign the expected_fingerprint argument instead of copying it into a
local, and give existing_secret its None default before the source
checks instead of in an else branch.

Behavior is unchanged. The installed package drops from 5161 to 5159
logical review lines, which keeps the integrated #7/#42/#57/#46/#80/#81
tip under the <5200 budget.

Security: the record gate still runs before any card is generated or
shown, and interrupts at that gate still raise _WalletSetupInterrupted.

Validation: ruff check, ruff format --check, mypy src/codex32, and
pytest (918 passed, with and without -O).

Refs #81, #38.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018az69UX4773mYohXAtE8kD

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted security release-gate re-review: ACK 054e8d9.

The verify-before-mutate finding is fixed on this exact head. BitcoinCore.initialize() validates the recovered seed against the independently recorded fingerprint before _select() or any wallet RPC/mutation; the mismatch regression leaves the wallet call path untouched. The explicit no-record fallback remains available, while ms32 wallet and ms32 create --existing keep the recovered fingerprint hidden until the independent record/no-record decision. Focused exact-head checks for mismatch, no-record, hidden-fingerprint, and existing-seed record gating passed (5 tests).

This replay has the same stable patch-id b1be2d64… as the previously reviewed 37eef4d and a7efaae versions. Exact-head CI is green, including the Core fixture, and the stack remains within the strict review-size gate at 5,196 logical lines on top of #42.

No remaining security/code-review blocker found. Human stack order remains #42 → #57 → #105 → #80 → #81 → #95.

Base automatically changed from codex/37-mixed-case-correction to reviewability-v1 October 2, 2026 06:31
Gate restore and existing-seed wallet initialization on the independently recorded BIP32 master fingerprint before any Bitcoin Core wallet mutation. Keep the correction path from disclosing or reusing a fingerprint derived from the candidate being authenticated.

Fixes #30.
@BenWestgate
BenWestgate force-pushed the 30-recorded-fingerprint-gate branch from 054e8d9 to 115f2c2 Compare October 2, 2026 08:57
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

BenWestgate pushed a commit that referenced this pull request Oct 2, 2026
Reassign the expected_fingerprint argument instead of copying it into a
local, and give existing_secret its None default before the source
checks instead of in an else branch.

Behavior is unchanged. The installed package drops from 5161 to 5159
logical review lines, which keeps the integrated #7/#42/#57/#46/#80/#81
tip under the <5200 budget.

Security: the record gate still runs before any card is generated or
shown, and interrupts at that gate still raise _WalletSetupInterrupted.

Validation: ruff check, ruff format --check, mypy src/codex32, and
pytest (918 passed, with and without -O).

Refs #81, #38.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018az69UX4773mYohXAtE8kD
BenWestgate pushed a commit that referenced this pull request Oct 2, 2026
`ms32 wallet` and `ms32 create --existing` hid the recovered master
fingerprint while confirming a correction (#57), so a wrong correction
was caught only after the operator accepted it and typed the record.

Ask for the record first: before the shares in `ms32 wallet` and before
the seed in `ms32 create --existing`. A correction that completes the
secret then says whether it matches the record, without showing the
fingerprint, and the record picks between equally likely corrections.
The final identity check, the retry on mismatch and the Enter path for
no record work as before; without a record nothing is shown until the
recordless gate. Ctrl-C at the moved prompt still says the existing
cards are valid.

Closes #91

Claude-Session: https://claude.ai/code/session_015CuLXqAvAovfoVcUmmogwa

@BenWestgate BenWestgate left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex current-head release-gate re-review: ACK 115f2c2 as the restore-authentication stack unit.

The current diff still enforces the key invariant: BitcoinCore.initialize() calls verify_identity() before wallet selection or mutation; ms32 wallet and ms32 create --existing suppress recovered-fingerprint disclosure until the independent record/no-record decision; declining the recordless path terminates the attempt. All existing inline findings are resolved. Exact-head Python-package run 658 and Bitcoin Core wallet-fixture run 32 both succeeded.

Known downstream requirement: #80 must remain in the frozen stack because it corrects the no-record wording when RIPEMD-160 is unavailable. That does not weaken this PR's verify-before-mutate behavior. No new blocker found in this current diff; human review remains required before integration.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command-line interface behavior. area: security Security invariants, hardening, and security-sensitive boundaries. area: wallet/core Wallet integration and Bitcoin Core boundaries. gate: adversarial review Resolve, merge, or explicitly defer before the next full adversarial review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant